package geetest

import (
	"crypto/md5"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"encoding/hex"
	"math/rand"
	"time"
	"encoding/json"
)

const (
	VERSION      = "0.1"
	BASE_URL     = "http://api.geetest.com/get.php"
	API_SERVER   = "http://api.geetest.com/validate.php"
	API_REGISTER = "http://api.geetest.com/register.php"

	ClientTypeWEB = "web"
	ClientTypeAPP = "native"

	//正常验证模式
	ValidateTypeSucc = 1
	//极验宕机验证模式
	ValidateTypeFail = 2
)

type GeeTest struct {
	CaptchId   string
	PrivateKey string
}

type InitOneResponseData struct {
	Success int `json:"success"`
	GT      string  `json:"gt"`
	Challenge string `json:"challenge"`
	NewCaptcha int `json:"new_captcha"`
}

type ValidateResponseData struct {
	Success int `json:"success"`
	GT      string  `json:"gt"`
	Challenge string `json:"challenge"`
	NewCaptcha int `json:"new_captcha"`
}

func NewGeeTest(captchId, privateKey string) GeeTest {
	return GeeTest{
		PrivateKey: privateKey,
		CaptchId:   captchId,
	}
}

//初始化1
//Auth: Xu
func InitFirst(captchId, privateKey string , captcha int, clientType string)(response InitOneResponseData){
	newGeeTest := NewGeeTest(captchId, privateKey)
	challenge  := newGeeTest.Challenge(captcha, clientType)
	if len(challenge) != 32 {
		response = newGeeTest.FailProcess(captcha)
	}
	response = newGeeTest.SuccessProcess(challenge, captcha )

	return
}
type validateResponse struct{
	Seccode string `json:"seccode"`
}
//正常验证
//Auth: Xu
func (geeTest GeeTest) SuccessValidate(challenge, validate, seccode string, clientType string)bool{
	if len(validate) != 32 {
		return false
	}
	if validate != geeTest.md5Value(geeTest.PrivateKey+"geetest"+challenge) {
		return false
	}

	values := url.Values{
		"client_type" : {clientType},
		"seccode": {seccode},
		"timestamp": {strconv.FormatInt(time.Now().Unix(),10)},
		"challenge": {challenge},
		"captchaid": {geeTest.CaptchId},
		"json_format":{"1"},
		"sdk"     : {"php_3.0.0"},
	}

	backInfo := geeTest.postValue(API_SERVER, values)
	var response validateResponse
	json.Unmarshal([]byte(backInfo), &response)
	return response.Seccode == geeTest.md5Value(seccode)
}

//极验宕机验证
//Auth: Xu
func (geeTest GeeTest) FailValidate(challenge, validate, seccode string)bool{
	if Md5Str(challenge) == validate{
		return true
	} else {
		return false
	}
}


//正常情况下的响应
//Auth: Xu
func (geeTest GeeTest) SuccessProcess(challenge string , captcha int)(response InitOneResponseData){
	md5Challenge := Md5Str(challenge+geeTest.PrivateKey)
	response = InitOneResponseData{
		1, geeTest.CaptchId , md5Challenge, captcha,
	}
	return
}

//宕机情况下的响应
//Auth: Xu
func (geeTest GeeTest) FailProcess(captcha int)(response InitOneResponseData){
	rnd1 := Md5Str(strconv.Itoa(rand.Intn(100)))
	rnd2 := Md5Str(strconv.Itoa(rand.Intn(100)))
	md5Challenge := rnd1+rnd2[0:2]
	response = InitOneResponseData{
		0, geeTest.CaptchId , md5Challenge, captcha,
	}
	return
}

func (geeTest GeeTest) Challenge(captcha int, clientType string) string {
	captchaStr := strconv.Itoa(captcha)
	return geeTest.get(fmt.Sprintf("%s?gt=%s&new_captcha=%s&client_type=%s", API_REGISTER, geeTest.CaptchId, captchaStr , clientType))
}

func (geeTest GeeTest) md5Value(values string) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(values)))
}

func (geeTest GeeTest) get(url string) string {
	resp, err := http.Get(url)
	if err != nil {
		return ""
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return ""
	}

	return string(body)
}

func (geeTest GeeTest) postValue(host string, values url.Values) string {
	resp, err := http.PostForm(host, values)
	if err != nil {
		return ""
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return ""
	}

	return string(body)
}

func Md5Str(text string) string {
	c := md5.New()
	c.Write([]byte(text))
	return hex.EncodeToString(c.Sum(nil))
}
